/*

ORBITER MODULE: Bump 

Project: surface-physics : http://code.google.com/p/surface-physics/

Description :This file implements the Bump plugin class for the
Orbiter Flight Simulation software. It creates the physics engine that
maintains the entire physics world and its partitions.

Written by Abhijit Nandy
License : Apache License 2.0

*/


#define ORBITER_MODULE

/*//Memory leak debugging
#define _CRTDBG_MAP_ALLOC
#include <stdlib.h>
#include <crtdbg.h>
*/



//Windows threading headers
#include <process.h>

// Enabled memory leak checks by un-commenting, ensure Visual Leak Detector is installed
//#include <vld.h>

//Orbiter Headers
#include "orbitersdk.h"


//Orbiter base management
#include "BulletBase/Bump_BulletBase.h"

//Required to initialize the GLUT part of the engine and the engine itself
#include "Visualizer/GlutStuff.h"
#include "Engine/PhysicsEngine.h"
#include "Visualizer/GLDebugDrawer.h"

#include <GL/freeglut.h>

#include "GUI/BaseSelector.h"

#include "OrulexSDK.h"/*<ORU>*/


/**
 * ==============================================================
 * This module is loaded only once and is shared by all bases and
 * vessels in a scenario. The bases and scenarios insert objects
 * into the physics world created by this plugin and
 * read back their transforms from it, as needed.
 * ==============================================================
 */


/* Thread controls */
const unsigned int SECONDARY_WAIT_COUNT = 50;
HANDLE hThread;
unsigned threadID;
bool renderingThreadRunning  = false;
bool runGLUT = false;

//Semaphore in visualizer mode
bool stepSim = false;

//#define SINGLE_STEP_PHYSICS


/**
 * Orbiter Interface
 *
 *
 * Operation mode
 * PhysicsMode mode : controls whether to display the GLUT based visualizer or not
 *
 * TerrainType terrainType : This is temporary, used to decide which kind of terrain
 * 					will be loaded for testing the BulletBaseVehicle
 */

PhysicsMode mode = PM_SILENT;


/**
 * The physics engine object, kept global to allow access from callback functions
 * and thread function
 */
PhysicsEngine *phyEngine;


/* Debugging stuff */
char output[1000];
static GLDebugDrawer sDebugDraw;

/* Config file for module Bump */
static char *cfgfile = "Modules\\Bump.cfg";

#ifdef SINGLE_STEP_PHYSICS
	bool singleStep = true;
#else
	bool singleStep = false;
#endif

/**
 * Maximum time warp passed on to Bullet
 */
const double MAX_SUPPORTED_TIME_WARP = 10.f;


/**
 * GUI Dialog Pointer
 */
BaseSelector *gBaseSelector = 0;   // scenario editor instance pointer

// These need to be global so the gui can access them
const int MAX_BASES = 2;
Bump_BulletBase *base[MAX_BASES];
int gNumBases = 0;

/* ==============================================================
 * Bump class interface
 * ==============================================================
 */


class Bump: public oapi::Module
{
	// We try to keep as little as possible in global scope
	float lastBoxHt;
	const float MAX_BASE_AABB_HEIGHT;
	btVector3 nextFreePos;

	void 	getBases();
	btVector3&	getNextFreeBox(void);


public:
	Bump (HINSTANCE  hModule) : oapi::Module (hModule), MAX_BASE_AABB_HEIGHT(500.) {}
	~Bump(){}
	
	
	//Module Overrides
	void	clbkPreStep(double SimT, double SimDT, double MJD);	
	void 	clbkPostStep(double SimT, double SimDT, double MJD);
	void	clbkSimulationStart(RenderMode mode);
	void	clbkSimulationEnd();
	void	clbkDeleteVessel(OBJHANDLE hVessel);
	void	clbkLoadState(FILEHANDLE scn);
	void	clbkSaveState(FILEHANDLE scn);
	void 	clbkTimeAccChanged(double  new_warp, double  old_warp);
};

// This is used, do not remove, see InitModule()
Bump *bump;

/**
 * Called only to start the visual debugger and update physics in a separate thread.
 * The visualizer must run in a separate thread as it enters the GLUT loop.
 * Uses the time delta from Orbiter.
 * Global renderingThreadRunning    : set to true as long as this thread is active, reset at exit
 * Global runGLUT : controls physics exit, used to exit from GLUT loop
 *
 */
unsigned __stdcall startPhysicsThread( void *arg )
{
	int rv = 0;
	int argc = 1;
	char *argv[1];

	renderingThreadRunning = true;

	//oapiWriteLog("startPhysicsThread: Secondary engaged ! ");

	argv[0] = new char[10];
	strcpy(argv[0], "Orbiter");

	phyEngine->getDynamicsWorld()->setDebugDrawer(&sDebugDraw);
	runGLUT = true;
	rv = glutmain(argc, argv, 1024, 600,"Bump Physics Visualizer : Powered by Bullet", phyEngine);

	delete argv[0];


	renderingThreadRunning = FALSE;
	oapiWriteLog("startPhysicsThread: Visualizer exiting ! ");
	return rv;

}


/**
 * ==============================================================
 * Overloaded callback functions
 * ==============================================================
 */


/**
 * --------------------------------------------------------------
 * Plugin initialization
 * --------------------------------------------------------------
 */
DLLCLBK void InitModule  ( HINSTANCE  hModule   )  
{
	oapiWriteLog("Bump: Initializing !");


	InitOrulexSDK();/*<ORU>*/


	//_CrtSetDbgFlag ( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF );

	int intMode = 0;

	//Read the mode from config file
	FILEHANDLE hFile = oapiOpenFile(cfgfile, FILE_IN, CONFIG);
	if (oapiReadItem_int(hFile, "Mode", intMode) ) {
		
		mode = (PhysicsMode)intMode;
		oapiWriteLogV("Bump: Read the mode successfully as %d", mode);
	}
	else
		mode = PM_SILENT;

	oapiCloseFile (hFile, FILE_IN);

	// Create selector instance
	gBaseSelector = new BaseSelector (hModule);

	// Register custom dialog controls
	oapiRegisterCustomControls (hModule);

	bump = new Bump (hModule);
	oapiRegisterModule(bump);
	
}


/**
 * --------------------------------------------------------------
 * Plugin cleanup
 * --------------------------------------------------------------
 */
DLLCLBK void ExitModule  ( HINSTANCE  hModule   )  
{		
	oapiWriteLog("Bump: Exiting");	

	// Clear the GUI
	delete gBaseSelector;

	
}

// ==============================================================
// Write some parameters to the scenario file

DLLCLBK void opcSaveState (FILEHANDLE scn)
{
	oapiWriteLog("opcSaveState : called");
	bump->clbkSaveState(scn);

	int myprm = 1;
	oapiWriteScenario_int (scn, "Param", myprm);
}

// ==============================================================
// Read custom parameters from scenario

DLLCLBK void opcLoadState (FILEHANDLE scn)
{
	oapiWriteLog("opcLoadState : called");
	bump->clbkLoadState(scn);

	/*char *line;
	while (oapiReadScenario_nextline (scn, line)) {
		if (!_strnicmp (line, "Param", 5)) {
			sscanf (line+5, "%d", &myprm);
		}
	}*/
}

/**
 * Called to refresh list of objects(only Bases) at the beginning of the sim
 *
 * This function has the following policy:
 * 1. If a vessel of className BulletBase is detected it will create a Bump_BulletBase
 * 		object to manage that base. The Bump_BulletBase object takes over all further management.
 * 2. If any other class ignore it.
 *
 * Currently only Brighton Beach on the Moon gets a BulletBase VESSEL
 *
 * TODO: If other vessel, look for a physics file associated with it
 * TODO: Look for bases and create a BulletBase instead of expecting one in the scenario file
 *
 */
void Bump::getBases()
{
	char strName[MAX_STRLEN];


	//oapiWriteLog("Bump: getBases : Refreshing base list------------------");


	//----------------Only Moon Bases for now, all bodies later--------------------------

	// Objects can be vessels, planets, moons or suns. NOT BASES
	for (unsigned int i = 0; i < oapiGetObjectCount(); ++i) {
	    OBJHANDLE hObjRbody = oapiGetObjectByIndex (i);

	    // OBJTP_PLANET planet (used for all celestial bodies that are not stars, including moons, comets, etc.)
	    // OBJTP_SURFBASE doesnt work, no surface bases returned
	    if (oapiGetObjectType(hObjRbody) == OBJTP_PLANET) {

	    	oapiGetObjectName(hObjRbody, strName, MAX_STRLEN);

	    	//oapiWriteLogV("Bump: getBases : Checking for bases on : %s ", strName);

	    	int numBases = oapiGetBaseCount(hObjRbody);

	    	for (int j = 0; j < numBases; ++j) {

	    		OBJHANDLE hObjBase = oapiGetBaseByIndex (hObjRbody, j);
				oapiGetObjectName(hObjBase, strName, MAX_STRLEN); 

				//oapiWriteLogV("Bump: getBases : Found base : %s", strName);

				if (strcmp(strName, "Brighton Beach") == 0 ||
					strcmp(strName, "Olympus") == 0) {

					// TODO : rethink this interface, just pass 1 structure initialized to diff stuff
					base[gNumBases] = new Bump_BulletBase();
					base[gNumBases]->clbkSetClassCaps(hObjBase, hObjRbody);
					base[gNumBases]->setDynamicsWorld(phyEngine->getDynamicsWorld());
					base[gNumBases]->setCollisionShapesArray(phyEngine->getCollisionShapesArray()); // seems pointless now
					base[gNumBases]->setPhysicsWorldOffset(getNextFreeBox());

					++gNumBases;
				}
			}

	    }


	 }

	//oapiWriteLog("Bump: getBases : Finished setting up bases------------------");

}


/**
 * Allocates sim boxes
 *
 */
btVector3&
Bump::getNextFreeBox(void)
{

	nextFreePos.setValue(0., lastBoxHt, 0.);

	//oapiWriteLogV("Bump::getNextFreeBox: Returned next free box at : y = %f", lastBoxHt);

	lastBoxHt += MAX_BASE_AABB_HEIGHT;

	return nextFreePos;
}

/**
 * Called before advancing Orbiter sim state
 * Reads base command structure for all BulletBases
 * Steps the physics world
 * Sends transform message to BB & BBV (BulletBase & BulletBaseVehicle)
 */
void Bump::clbkPreStep(double SimT, double SimDT, double MJD) 
{
	//oapiWriteLogV("Bump::clbkPreStep:  entry");

	//Step the physics world, open the semaphore for visualizer mode(its disabled by the thread, till next
	//Orbiter step)
	
	//Prestep not needed as the job of prestep() can be done in the iteration before this
	//(the Bullet world is persistent & will keep the last change made anywayz)
	//Unless there is something imp to be done before the physics step such as AABB comparison ?
	//brightonBeach.prestep(SimT, SimDT, MJD);


	//Must wait till rendering thread has halted if debug mode
	//May be removed for speed later
	while(renderingThreadRunning && stepSim)
		Sleep(10);

	//Actually step the physics here, this where we would step all the bases
	//Only a single base is supported here

	if (Bump_BulletBase::mTimeWarpFactor < (MAX_SUPPORTED_TIME_WARP + 2.)) {

#ifdef SINGLE_STEP_PHYSICS
		if (singleStep)
#endif
			phyEngine->clientMoveAndDisplay();

#ifdef SINGLE_STEP_PHYSICS
		singleStep = false;
#endif


		//Prints the current state of the physics engine
		//phyEngine->print();



		//Update all bases(they will take care of updating their constituent objects)
		//TODO: Update all VESSELs too when they have physics in them

		//Here the actual rigid bodies are added, dynamics world is empty before this
		for (int i = 0; i < gNumBases; i++)
			base[i]->clbkPreStep(SimT, SimDT, MJD);
		//olympus->clbkPreStep(SimT, SimDT, MJD);
	}


	//oapiWriteLogV("Bump::clbkPreStep:  end");




}


void Bump::clbkPostStep(double SimT, double SimDT, double MJD)
{
	//VesselCollider adds/removes objects dynamically from physics world
	//So stepSim MUST NOT BE SET to TRUE BEFORE bases have finished updating
	//else rendering thread will screw up !!

	//oapiWriteLogV("Bump::clbkPostStep:  :  entry");

	for (int i = 0; i < gNumBases; i++)
		base[i]->clbkPostStep(SimT, SimDT, MJD);

	stepSim = true;

	//oapiWriteLogV("Bump::clbkPostStep:  :  end");
}


/**
 * Called whenever a simulation is started from the Launchpad &
 * when all objects have been inserted
 * Used to get the number of objects in the current sim & their handles.
 */
void Bump::clbkSimulationStart(RenderMode renderMode) 
{
	oapiWriteLog("Bump: clbkSimulationStart : Sim starting");

	//Setup some Bump stuff
	lastBoxHt = 0.;
	nextFreePos.setValue(0., 0., 0.);

	phyEngine = new PhysicsEngine();

	//Initialize physics in Orbiter thread always(whatever the mode)
	//Any changes to the dynamics world is done in this thread
	phyEngine->initPhysics();

	//Start a thread if visualizer requested, else physics in same thread
	if (mode == PM_VISUALIZE) {
		hThread = (HANDLE)_beginthreadex(NULL , 0, &startPhysicsThread, NULL, 0, &threadID);
		oapiWriteLogV("Bump: Started VISUALIZATION thread ID : %d", threadID);
	}
	else
		oapiWriteLogV("Bump: Running silent....");

	//Setup bases
	getBases();

}


/**
 * Called when sim is ended, used to free memory.
 * The module remains in memory throughout the Orbiter main
 * process's lifetime, so its important to reset variables
 * that track individual sims at every simulation exit.
 */
void Bump::clbkSimulationEnd()   
{
	int renderingThreadWaitCount = SECONDARY_WAIT_COUNT;

	oapiWriteLog("Bump: clbkSimulationEnd : Sim ending");

	Bump_BulletBase::simIsEnding = true;




	//Wait for GLUT to exit the moveAndDisplay() for some maximum time if in VISUALIZE mode
	if (renderingThreadRunning) {
		runGLUT = FALSE;

		while(renderingThreadRunning && renderingThreadWaitCount > 0) {
			runGLUT = FALSE;
			oapiWriteLog("Bump: Secondary thread still running... ");
			renderingThreadWaitCount--;
			Sleep(100);
		}

		if (renderingThreadWaitCount == 0) {
			TerminateThread(hThread, 0);
			oapiWriteLog("Bump: Secondary thread force terminated. ");
		}
	}


	//Delete bases here
	for (int i = 0; i < gNumBases; i++)
		delete base[i];
	//delete olympus;

	gNumBases = 0;

	oapiWriteLogV("Bump::clbkSimulationEnd : About to exit ");


	//Delete engine after bases exited or deleted
	delete phyEngine;


}


/**
 * Called whenever a vessel is deleted from the sim
 * TODO Get vessel and base data again at this point
 */
void
Bump::clbkDeleteVessel(OBJHANDLE hVessel)
{
	oapiWriteLog("Bump: clbkDeleteVessel : Vessel deleted.");

	VESSEL *v = (VESSEL*)(oapiGetVesselInterface(hVessel));
	char *vesselClass = v->GetClassName();
	oapiWriteLogV("Bump: clbkDeleteVessel : Vessel deleted was of class %s", vesselClass);

	if (strcmp(vesselClass, "BulletBase") == 0) {
		return;
	}
}

/**
 * Called to load state of objects which are not Orbiter vessels
 */
void
Bump::clbkLoadState(FILEHANDLE scn)
{
	oapiWriteLog("Bump: clbkLoadState : CALLED");
}


/**
 * Save state of objects which are not Orbiter vessels
 */
void
Bump::clbkSaveState(FILEHANDLE scn)
{
	oapiWriteLog("Bump: clbkSaveState : CALLED");
}


/**
 * Notification of time acceleration. Currently used to attach all base-controlled
 * vessels & prepare for the 'ride' through high time warps.
 */
void
Bump::clbkTimeAccChanged(double  new_warp,
		  	  	  	  	 double  old_warp)

{
	oapiWriteLog("Bump: clbkTimeAccChanged : CALLED");
	Bump_BulletBase::mTimeWarpFactor = new_warp;
}
